package org.jivesoftware.smack;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;

import com.alibaba.fastjson.JSONObject;
import com.senyint.imsdk.AsyncPakcetQueueHandler;
import com.senyint.imsdk.ISdkConfig;
import com.senyint.imsdk.SendPacketException;
import com.senyint.imsdk.http.AsyncHttpHandler;
import com.senyint.imsdk.http.ISimpleHttpService;
import com.senyint.imsdk.http.SimpleHttpRequest;
import com.senyint.imsdk.listener.SendPacketListener;
import com.senyint.imsdk.packet.AttachmentPacketExtension;
import com.senyint.imsdk.packet.DeliverySentManager;
import com.senyint.imsdk.util.PacketUtil;

/**
 * 
 * @author hui
 *
 */
public class Conversation implements java.io.Serializable{
    /**
	 * 
	 */
	private static final long serialVersionUID = 1822370367335351651L;
	protected static final String TAG = Conversation.class.getSimpleName();
	private ConversationManager chatManager;
    private String conversation;
    private String participant;
    private String participantJID = null ;
    private String topic ;

    private Map<PacketInterceptor, PacketFilter> interceptors = new WeakHashMap<PacketInterceptor, PacketFilter>();

    private final Set<ConversationMessageListener> listeners = new CopyOnWriteArraySet<ConversationMessageListener>();
    
    /**
     * 异步发送消息对象
     */
    private AsyncPakcetQueueHandler<Message> asyncMessageSender = null ;
    
    private SendPacketListener mSendPacketListener = null ;
    
    private ISdkConfig sdkCfg = null ;
    
    Conversation(final ConversationManager chatManager, ISdkConfig cfg,String participant,String topic) {
        this.chatManager = chatManager;
        this.sdkCfg = cfg;
        this.participant = participant;
        this.topic = topic ;
        
        this.conversation = PacketUtil.createConversationID(sdkCfg.getUserName(),participant,topic);
//        asyncPacketHandler = new DefaultAysncMessageHandler(){
//    		@Override
//    		public void onProcessInBackgroud(Message message) {
//    			Log.d(TAG,"conversation handler:"+message.toXML());
////    			super.onProcessInBackgroud(message);
//    	        for (ConversationMessageListener listener : listeners) {
//    	            listener.processMessage(Conversation.this, message);
//    	        }
//    		}
//    		
//    	};
    	
    	asyncMessageSender = new AsyncPakcetQueueHandler<Message>(){
			@Override
			public void onProcessInBackgroud(Message message) {
				try {
					chatManager.sendMessage(Conversation.this,message);
					onSendSuccess(message,null);
				} catch (SendPacketException e) {
					e.printStackTrace();
					onSendFailed(message,null);
				}
			}
    	};
    	
    }

	public String getConversation() {
        return conversation;
    }
	
	protected String getParticipantJID(){
		if(participantJID == null&& participant!=null){
			participantJID = participant + "@" +sdkCfg.getServiceName();
		}
		return participantJID ;
	}

    public String getParticipant() {
        return participant;
    }
    
    public String getTopic() {
        return topic;
    }

	public SendPacketListener getSendPacketListener() {
		return mSendPacketListener;
	}

	public void setSendPacketListener(SendPacketListener mSendPacketListener) {
		this.mSendPacketListener = mSendPacketListener;
	}
	
	public Message sendMessage(String text) throws SendPacketException {
//        Message message = new Message(getParticipantJID(), Message.Type.chat);
//        message.setBody(text);
//        sendMessage(message);
        return sendAttachmentMessage(text,Message.Type.chat,null);
    }

	/**
	 * 发送带附件信息的消息
	 * @param tojid		接收人
	 * @param msg		消息内容
	 * @param type		消息类型(chat/groupchat)
	 * @param ape		消息拓展体,发送自定义封装的附件信息
	 * @throws SendPacketException
	 */
	public Message sendMessage(String msg,AttachmentPacketExtension ape) throws SendPacketException {
		return this.sendAttachmentMessage(msg, Message.Type.chat,ape);
	}
	
	/**
	 * 发送群聊信息(groupchat)
	 * @param tojid
	 * @param msg
	 * @throws SendPacketException
	 */
	public Message sendGroupMessage( String msg) throws SendPacketException {
		return sendGroupMessage(msg, null);
	}
	
	public Message sendGroupMessage( String msg, AttachmentPacketExtension ape) throws SendPacketException {
		return this.sendAttachmentMessage(msg, Message.Type.groupchat, ape);
	}
	
	/**
	 * 发送本地附件信息<br>
	 * 文件上传后将会更新AttachmentPacketExtension's content，将文件地址url写入content。<br>
	 * 建议如果有自定义内容，则需要将相关内容写入remark。
	 * 
	 * @param tojid			接收人
	 * @param msg			消息体
	 * @param type			消息类型,chat/groupchat
	 * @param filePath		文件绝对路径
	 * @param localApe		消息拓展
	 * @throws SendPacketException
	 */
	public Message sendAttachmentMessage(String msg,Message.Type type,final AttachmentPacketExtension localApe) throws SendPacketException {
		final Message message = new Message();
		if(message.getPacketID() == null){
			message.setPacketID(Packet.nextID());
		}
		message.setBody(msg);
		message.setType(type);
		message.setFrom(sdkCfg.getUserJID());
		message.setConversation(conversation);
		message.setTo(getParticipantJID());
        if (localApe != null) {
			message.addExtension(localApe);
		}
		
        if(topic != null){
        	message.setTopic(topic);
        }
		/**
		 * 添加送达服务器的回执
		 */
		DeliverySentManager.addDeliverySentRequest(message);
		
		final String filePath = localApe==null?null: localApe.getLocalFile();
		if(filePath != null){
			int fileType = ISdkConfig.FILE_TYPE_IMAGE ;
			if(filePath.endsWith("amr")){
				fileType = ISdkConfig.FILE_TYPE_IMAGE;
			}else{
				fileType = ISdkConfig.FILE_TYPE_UNKOWN;
			}
			File file = new File(filePath);
			if(!file.isFile()||!file.exists()||file.length()> 5*1024*1024){
				throw new SendPacketException("file not found or file too large!");
			}
			
			
			SimpleHttpRequest simpleHttpRequest = new SimpleHttpRequest();
			String user = sdkCfg.getUserName();
			simpleHttpRequest.addParameter("uid", user);
			simpleHttpRequest.addParameter("token", sdkCfg.getPassword());
			simpleHttpRequest.addParameter("fileType", "" + fileType);
			// 将文件放入参数中
			simpleHttpRequest.addFile("file", file);
			simpleHttpRequest.setRequestType(SimpleHttpRequest.REQUEST_UPLOAD);
			simpleHttpRequest.setUrl(ISdkConfig.APP_UPLOAD_URI);
			
			simpleHttpRequest.setCallback(new ISimpleHttpService.HttpCallback() {
				@Override
				public void onSuccess(byte[] response) {
					try {
						JSONObject json = JSONObject.parseObject(new String(response));
						if(localApe.getDuration() > 0){
							json.getJSONObject("content").put("duration", ""+localApe.getDuration());
						}
						
						localApe.setContent(json.getJSONObject("content").toString());
						if (localApe != null) {
							message.addExtension(localApe);
						}
						localApe.setLocalFile(null);
						sendMessage(message);
//						onSendSuccess(message,localApe);
					} catch (Exception e) {
						e.printStackTrace();
						onSendFailed(message,localApe);
					}
				}
				
				@Override
				public void onFailure(String msg) {
					onSendFailed(message,localApe);
				}
			});
			
			AsyncHttpHandler.getInstance().process(simpleHttpRequest);
			
		}else{
			sendMessage(message);
//			onSendSuccess(message,localApe);
		}
		return message ;
	}
	
	public Message sendMessage(final Message message) {
		if(message == null){
			throw new NullPointerException("Packet is null.");
		}
		
        for(Map.Entry<PacketInterceptor, PacketFilter> interceptor : interceptors.entrySet()) {
            PacketFilter filter = interceptor.getValue();
            if(filter != null && filter.accept(message)) {
                interceptor.getKey().interceptPacket(message);
            }
        }
        
//		chatManager.sendMessage(this, message);
        asyncMessageSender.onProcessPacket(message);
        
		return message;
	}
	
	
//	/**
//	 * 发送本地附件信息<br>
//	 * 文件上传后将会更新AttachmentPacketExtension's content，将文件地址url写入content。<br>
//	 * 建议如果有自定义内容，则需要将相关内容写入remark。
//	 * 
//	 * @param tojid			接收人
//	 * @param msg			消息体
//	 * @param type			消息类型,chat/groupchat
//	 * @param filePath		文件绝对路径
//	 * @param localApe		消息拓展
//	 * @throws SendPacketException
//	 */
//	public Message sendAttachmentMessage( String msg,Message.Type type,final AttachmentPacketExtension localApe) throws SendPacketException {
//		final Message message = new Message();
//		message.setBody(msg);
//		message.setType(type);
//		final String filePath = localApe.getLocalFile();
//		if(filePath != null){
//			int fileType = ISdkConfig.FILE_TYPE_IMAGE ;
//			if(filePath.endsWith("amr")){
//				fileType = ISdkConfig.FILE_TYPE_VOICE;
//			}else if(filePath.endsWith("jpg")||filePath.endsWith("png")||filePath.endsWith("jpeg")||filePath.endsWith("gif")||filePath.endsWith("bmp")){
//				fileType = ISdkConfig.FILE_TYPE_IMAGE;
//			}else{
//				fileType = ISdkConfig.FILE_TYPE_UNKOWN;
//			}
//			File file = new File(filePath);
//			if(!file.isFile()||!file.exists()||file.length()> 10*1024*1024){
//				throw new SendPacketException("file not found or file too large!");
//			}
//			
//			FileUploadThread upload = new FileUploadThread(filePath,fileType);
//			upload.setUploadCallBack(new FileUploadThread.UploadCallBack(){
//				@Override
//				public void onFailure(Throwable t, int errorNo, String strMsg) {
//					super.onFailure(t, errorNo, strMsg);
//					onSendFailed(message,localApe);
//				}
//				
//				@Override
//				public void onStart() {
//					super.onStart();
//				}
//	
//				@Override
//				public void onSuccess(String t) {
//					super.onSuccess(t);
//					try {
//						JSONObject json = new JSONObject(t);
////						String url = ISdkConfig.APP_DOWNLD_URI + "?url=" + json.getJSONObject("content").getString("url");
////						String url = json.getJSONObject("content").;
//						
//						json.getJSONObject("content").put("duration", ""+localApe.getDuration());
//						localApe.setContent(json.getJSONObject("content").toString());
//						if (localApe != null) {
//							message.addExtension(localApe);
//						}
//						localApe.setLocalFile(null);
//						sendMessage(message);
//					} catch (SendPacketException  e) {
//						e.printStackTrace();
//						onSendFailed(message,localApe);
//					} catch (Exception e) {
//						e.printStackTrace();
//						onSendFailed(message,localApe);
//					}
//					
//				}
//			});
//			upload.startUpload();
//			
////			FileAsyncHandler.getInstance().process(filePath,fileType,new FileUploadThread.UploadCallBack(){
////				@Override
////				public void onFailure(Throwable t, int errorNo, String strMsg) {
////					super.onFailure(t, errorNo, strMsg);
////					onSendFailed(message,localApe);
////				}
////				
////				@Override
////				public void onStart() {
////					super.onStart();
////				}
////	
////				@Override
////				public void onSuccess(String t) {
////					super.onSuccess(t);
////					try {
////						JSONObject json = new JSONObject(t);
//////						String url = ISdkConfig.APP_DOWNLD_URI + "?url=" + json.getJSONObject("content").getString("url");
//////						String url = json.getJSONObject("content").;
////						
////						json.getJSONObject("content").put("duration", ""+localApe.getDuration());
////						localApe.setContent(json.getJSONObject("content").toString());
////						if (localApe != null) {
////							message.addExtension(localApe);
////						}
////						localApe.setLocalFile(null);
////						sendMessage(message);
////					} catch (SendPacketException  e) {
////						e.printStackTrace();
////						onSendFailed(message,localApe);
////					} catch (Exception e) {
////						e.printStackTrace();
////						onSendFailed(message,localApe);
////					}
////					
////				}
////			});
//		}else{
//			if (localApe != null) {
//				message.addExtension(localApe);
//			}
//			
//			return sendMessage(message);
//		}
//		return message ;
//	}
	
	protected void onSendSuccess(Message message,AttachmentPacketExtension localApe) {
		if(mSendPacketListener != null){
			mSendPacketListener.onSuccess(message,localApe);
		}
	}
	
	/**
	 * 发送失败
	 * @param message
	 * @param localApe
	 */
	protected void onSendFailed(Message message,AttachmentPacketExtension localApe) {
		if(localApe != null)
			message.addExtension(localApe);
		
		if(mSendPacketListener != null){
			mSendPacketListener.onFailed(message, localApe);
		}
	}

    /**
     * Adds a packet listener that will be notified of any new messages in the
     * chat.
     *
     * @param listener a packet listener.
     */
    public void addMessageListener(ConversationMessageListener listener) {
        if(listener == null) {
            return;
        }
        // TODO these references should be weak.
        listeners.add(listener);
    }

    public void removeMessageListener(ConversationMessageListener listener) {
        listeners.remove(listener);
    }

    public void clearMessageListener() {
        listeners.clear();
    }

    
    /**
     * Returns an unmodifiable collection of all of the listeners registered with this chat.
     *
     * @return an unmodifiable collection of all of the listeners registered with this chat.
     */
    public Collection<ConversationMessageListener> getListeners() {
        return Collections.unmodifiableCollection(listeners);
    }

    /**
     * Delivers a message directly to this chat, which will add the message
     * to the collector and deliver it to all listeners registered with the
     * Chat. This is used by the Connection class to deliver messages
     * without a thread ID.
     *
     * @param message the message.
     */
    public void deliver(Message message) {
        // Because the collector and listeners are expecting a thread ID with
        // a specific value, set the thread ID on the message even though it
        // probably never had one.
    	
//    	asyncPacketHandler.onProcessInBackgroud(message);
    	for (ConversationMessageListener listener : listeners) {
            listener.processMessage(Conversation.this, message);
        }
    	
    }

    public void addOutgoingMessageInterceptor(PacketInterceptor packetInterceptor, PacketFilter filter) {
        if (packetInterceptor != null) {
            interceptors.put(packetInterceptor, filter);
        }
    }

    @Override
    public boolean equals(Object obj) {
        return obj instanceof Conversation
                && conversation.equals(((Conversation)obj).getConversation())
                && participant.equals(((Conversation)obj).getParticipant() );
    }
    
    public static String bytes2hexstring(byte[] src) {
		StringBuilder stringBuilder = new StringBuilder("");
		if (src == null || src.length <= 0) {
			return null;
		}
		for (int i = 0; i < src.length; i++) {
			int v = src[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
		return stringBuilder.toString();
	}

}
